#include "Graphics/gpuprefabs.h"
#include "Graphics/renderer.h"
#include "SDL3_image/SDL_image.h"
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"

using namespace Jin;

struct AppContext final {

	enum {
		WIN_WIDTH = 800,
		WIN_HEIGHT = 600,
		WIN_FLAGS = 0,

		CIRCLE_COUNT = 10
	};

	SDLFramework sdlfx;
	SDLWindowUPtr mainWindow;
	GPUDeviceUPtr gpuDevice;
	GPUGraphicsPipelinePoolUPtr gplPool;
	GPUUploadBufferUPtr uploadBuffer;
	GPUStorageBufferUPtr vertexBuffer;
	GPUViewport viewport{};
	Color32 blendColor{ 0xffffffff };

	CameraVertexUniform cameraParams;
	CircleVertexUniform circleParams;
	std::vector<CircleVertex> circles;
	Uint32 gplID = GPL_AACIRCLE;

	AppContext() = default;
	~AppContext() = default;
	bool Init();
	void Run();
	void Cleanup();

private:
	bool _CreateMainWindow() {
		mainWindow = std::make_unique<SDLWindow>("testSDLGPU", WIN_WIDTH, WIN_HEIGHT, WIN_FLAGS);
		if (!*mainWindow) {
			return false;
		}
		sdlfx.SetFPS(200);
		sdlfx.onFPS = [this](Uint32 fps) {
			mainWindow->SetTitle(std::format("fps:{}, delta:{}", fps, sdlfx.deltaTime).c_str());
			};
		return true;
	}
	bool _CreateGPUDevice() {
		gpuDevice = std::make_unique<GPUDevice>(*mainWindow);
		if (!*gpuDevice) {
			return false;
		}
		gplPool = std::make_unique<GPUGraphicsPipelinePool>(*gpuDevice);
		if (*gplPool != GPL_NUM) {
			return false;
		}
		uploadBuffer = std::make_unique<GPUUploadBuffer>(*gpuDevice, (Uint32)(CIRCLE_COUNT * sizeof(CircleVertex)));
		if (!*uploadBuffer) {
			return false;
		}
		vertexBuffer = std::make_unique<GPUStorageBuffer>(*gpuDevice, (Uint32)(CIRCLE_COUNT * sizeof(CircleVertex)));
		if (!*vertexBuffer) {
			return false;
		}
		gpuDevice->SetSwapchainParameters(SDL_GPU_SWAPCHAINCOMPOSITION_SDR, SDL_GPU_PRESENTMODE_IMMEDIATE);
		return true;
	}
	void _UpdateData() {
		GPUCommandBuffer cmd{ *gpuDevice };
		if (!cmd) {
			return;
		}
		for (auto& circle : circles) {
			circle.position = { (float)WIN_WIDTH * SDL_randf(), (float)WIN_HEIGHT * SDL_randf() };
			circle.radius = 100.0f * SDL_randf();
			circle.color = { SDL_randf(), SDL_randf(), SDL_randf(), SDL_randf() };
		}
		//circles[0].position = { 10.0f, 10.0f };
		//circles[0].radius = 100.0f;
		//circles[0].color = { SDL_randf(), SDL_randf(), SDL_randf(), 1.0f/*SDL_randf()*/ };
		memcpy(GPUTransferBufferMapper{ *uploadBuffer, true }, circles.data(), CIRCLE_COUNT * sizeof(CircleVertex));
		GPUCopyPass{ cmd }.UploadToGPUBuffer({ *uploadBuffer, 0 }, { *vertexBuffer, 0, CIRCLE_COUNT * sizeof(CircleVertex) }, true);
	}
};

bool AppContext::Init() {
	if (!_CreateMainWindow()) {
		return false;
	}
	if (!_CreateGPUDevice()) {
		return false;
	}
	SDL_srand(0);
	viewport = { .x = 0, .y = 0, .w = (float)WIN_WIDTH, .h = (float)WIN_HEIGHT, .max_depth = 1.0f };
	cameraParams.camera = glm::ortho(0.0f, viewport.w, viewport.h, 0.0f, 0.0f, -1.0f);
	circleParams.thickness = 0.0f;
	circles.resize(CIRCLE_COUNT);
	_UpdateData();
	return true;
}

void AppContext::Run() {
	sdlfx.onFrame = [this]() {
		if (mainWindow->GetWindowFlags() & SDL_WINDOW_MINIMIZED) {
			return;
		}
		GPUCommandBuffer cmd{ *gpuDevice };
		if (!cmd) {
			return;
		}
		GPUSwapchainTexture swapchainTexture{ cmd, *mainWindow };
		if (!swapchainTexture) {
			return;
		}

		cmd.PushVertexUniformData(0, &cameraParams, sizeof(cameraParams));
		cmd.PushVertexUniformData(1, &circleParams, sizeof(circleParams));

		GPUColorTargetInfo colorTargetInfo{
			.texture = swapchainTexture,
			.clear_color = {0.2f, 0.3f, 0.3f, 1.0f},
			.load_op = SDL_GPU_LOADOP_CLEAR,
			.store_op = SDL_GPU_STOREOP_STORE
		};
		GPURenderPass renderPass{ cmd, &colorTargetInfo, 1 };
		if (!renderPass) {
			return;
		}
		renderPass.SetBlendConstants(blendColor);
		renderPass.SetViewport(viewport);
		//renderPass.SetScissor({ 100, 100, 100, 100 });
		renderPass.BindGraphicsPipeline((*gplPool)[gplID]);
		renderPass.BindVertexStorageBuffer(0, *vertexBuffer);
		renderPass.DrawPrimitives(CIRCLE_COUNT * 6, 1, 0, 0);
		};
	sdlfx.onEvent = [this](EventData& event) {
		if (event.type == SDL_EVENT_KEY_DOWN) {
			if (event.key.key == SDLK_G) {
				_UpdateData();
			}
			else if (event.key.key == SDLK_KP_PLUS) {
				circleParams.thickness += 0.5f;
			}
			else if (event.key.key == SDLK_KP_MINUS) {
				circleParams.thickness -= 0.5f;
			}
			else if (event.key.key == SDLK_UP) {
				viewport.y -= 10.0f;
			}
			else if (event.key.key == SDLK_DOWN) {
				viewport.y += 10.0f;
			}
			else if (event.key.key == SDLK_LEFT) {
				viewport.x -= 10.0f;
			}
			else if (event.key.key == SDLK_RIGHT) {
				viewport.x += 10.0f;
			}
			else if (event.key.key == SDLK_W) {
				viewport.h -= 10.0f;
			}
			else if (event.key.key == SDLK_S) {
				viewport.h += 10.0f;
			}
			else if (event.key.key == SDLK_A) {
				viewport.w -= 10.0f;
			}
			else if (event.key.key == SDLK_D) {
				viewport.w += 10.0f;
			}
			else if (event.key.key == SDLK_B) {
				auto state = gplPool->states[gplID];
				state.colorTargetDescriptionsID = CTD_BLENDMODE_BLEND;
				gplID = gplPool->GetPipelineID(state);
			}
			else if (event.key.key == SDLK_N) {
				auto state = gplPool->states[gplID];
				state.colorTargetDescriptionsID = CTD_BLENDMODE_NONE;
				gplID = gplPool->GetPipelineID(state);
			}
			else if (event.key.key == SDLK_COMMA) {
				gplID = GPL_CIRCLE;
			}
			else if (event.key.key == SDLK_PERIOD) {
				gplID = GPL_AACIRCLE;
			}
			//else if (event.key.key == SDLK_1) {
			//	blendColor.r += 0.1f;
			//	SDL_LogInfo(0, "blendColor.r : %f", blendColor.r);
			//}
			//else if (event.key.key == SDLK_KP_1) {
			//	blendColor.r -= 0.1f;
			//	SDL_LogInfo(0, "blendColor.r : %f", blendColor.r);
			//}
			//else if (event.key.key == SDLK_2) {
			//	blendColor.g += 0.1f;
			//	SDL_LogInfo(0, "blendColor.g : %f", blendColor.g);
			//}
			//else if (event.key.key == SDLK_KP_2) {
			//	blendColor.g -= 0.1f;
			//	SDL_LogInfo(0, "blendColor.g : %f", blendColor.g);
			//}
			//else if (event.key.key == SDLK_3) {
			//	blendColor.b += 0.1f;
			//	SDL_LogInfo(0, "blendColor.b : %f", blendColor.b);
			//}
			//else if (event.key.key == SDLK_KP_3) {
			//	blendColor.b -= 0.1f;
			//	SDL_LogInfo(0, "blendColor.b : %f", blendColor.b);
			//}
			//else if (event.key.key == SDLK_4) {
			//	blendColor.a += 0.1f;
			//	SDL_LogInfo(0, "blendColor.a : %f", blendColor.a);
			//}
			//else if (event.key.key == SDLK_KP_4) {
			//	blendColor.a -= 0.1f;
			//	SDL_LogInfo(0, "blendColor.a : %f", blendColor.a);
			//}
			cameraParams.SetOrthoViewport(viewport);
		}
		return true;
		};
	sdlfx.Run();
}

void AppContext::Cleanup() {
	gpuDevice->WaitIdle();
}

int main() {
	try {
		AppContext app;
		if (app.Init()) {
			app.Run();
		}
		app.Cleanup();
	}
	catch (const std::exception& e) {
		SDL_LogError(0, "App error! '%s'", e.what());
		return -1;
	}
	return 0;
}
